package com.middlegroup.pay.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.middlegroup.common.constants.MethodConstants;
import com.middlegroup.common.enums.ConfigKeyEnums;
import com.middlegroup.common.enums.ServiceType;
import com.middlegroup.common.model.*;
import com.middlegroup.common.utils.AesUtils;
import com.middlegroup.common.utils.Base64Utils;
import com.middlegroup.common.utils.RSAUtils;
import com.middlegroup.pay.entity.*;
import com.middlegroup.pay.mapper.InBillsMapper;
import com.middlegroup.pay.mapper.OutBillsMapper;
import com.middlegroup.pay.mapper.ThirdProtocolBindRecordMapper;
import com.middlegroup.pay.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.security.PrivateKey;
import java.util.Map;
import java.util.TreeMap;

/**
 * 查询服务类
 *
 * @author pc
 * @date 2019/1/25
 */
@Service
@Slf4j
public class QueryServiceImpl implements QueryService {

    @Resource
    private InBillsService inBillsService;

    @Resource
    private OutBillsService outBillsService;

    @Resource
    private SubjectService subjectService;

    @Resource
    private SystemCfgService systemCfgService;

    @Resource
    private ThirdQueryBillsService thirdQueryBillsService;

    @Resource
    private OutBillsMapper outBillsMapper;

    @Resource
    private InBillsMapper inBillsMapper;

    @Resource
    private SubjectPayService subjectPayService;

    @Resource
    private ThirdProtocolBindRecordMapper thirdProtocolBindRecordMapper;

    @Override
    public TransResult orderQuery(QueryRequest request) {
        TransResult transResult = new TransResult(request.getBillNo(), request.getMerchantBillNo(), 0, "查询中,请已transStatus为准");
        ServiceType serviceType = ServiceType.valueOf(request.getServiceType());
        String method = null;
        String merchantCode = null;
        switch (serviceType) {
            case DFQUERY:
                method = MethodConstants.DF_NOTIFY;
                OutBills outBills = getOutBills(request);
                if (null == outBills){
                    transResult.setMsg("未找到订单，请稍候查询!");
                    return transResult;
                }
                merchantCode = outBills.getMerchantCode();
                transResult = post(request);
                break;
            case DSQUERY:
                method = MethodConstants.DEDUCT_NOTIFY;
                InBills inBills = getInBills(request);
                merchantCode = inBills.getMerchantCode();
                transResult = post(request);
                break;
            case BINDCARDQUERY:
                method = MethodConstants.ORDERQUERY;
                request.setServiceType(ServiceType.BINDCARDQUERY.name());
                getBindCardBills(request);
                transResult = post(request);
                break;
            default:
                return transResult;
        }
        //判断是否回调业务方
        if (null == request.getCallback()){
            request.setCallback(0);
        }
        if (Integer.compare(1,request.getCallback()) == 0){
            NotifyRequest notifyRequest = new NotifyRequest();
            notifyRequest.setBillNo(transResult.getBillNo());
            notifyRequest.setMerchantBillNo(transResult.getMerchantBillNo());
            notifyRequest.setTransStatus(transResult.getTransStatus());
            notifyRequest.setMsg(transResult.getMsg());
            subjectPayService.notifyMerchant(merchantCode,method,notifyRequest);
        }
        return transResult;
    }

    /**获取出款订单
     * @param request
     * @return
     */
    private OutBills getOutBills(QueryRequest request){
        OutBills outBillsFind = new OutBills();
        outBillsFind.setMerchantBillNo(request.getMerchantBillNo());
        outBillsFind.setMerchantCode(request.getMerchantCode());
        OutBills outBills = outBillsMapper.selectOne(outBillsFind);
        if (null != outBills){
            request.setSubjectCode(outBills.getSubjectCode());
            request.setServiceType(ServiceType.DFQUERY.name());
        }
        return outBills;
    }
    /**获取收款订单
     * @param request
     * @return
     */
    private InBills getInBills(QueryRequest request){
        InBills inBillsFind = new InBills();
        inBillsFind.setMerchantBillNo(request.getMerchantBillNo());
        inBillsFind.setMerchantCode(request.getMerchantCode());
        InBills inBills = inBillsMapper.selectOne(inBillsFind);
        if (null != inBills){
            request.setSubjectCode(inBills.getSubjectCode());
            request.setServiceType(ServiceType.DSQUERY.name());
        }
        return inBills;
    }

    private ThirdProtocolBindRecord getBindCardBills(QueryRequest request){
        ThirdProtocolBindRecord inBillsFind = new ThirdProtocolBindRecord();
        inBillsFind.setMerchantBillNo(request.getMerchantBillNo());
        inBillsFind.setBillNo(request.getBillNo());
        ThirdProtocolBindRecord inBills = thirdProtocolBindRecordMapper.selectOne(inBillsFind);
        if (null != inBills){
            request.setChannelCode(inBills.getChannelCode());
            request.setSubjectCode(inBills.getSubjectCode());
            request.setServiceType(ServiceType.BINDCARDQUERY.name());
        }
        return inBills;
    }

    /**
     * 请求主体服务端
     * 对业务参数加密，请求加签名等
     *
     * @param request 交易参数
     * @return
     */
    private TransResult post(@Valid QueryRequest request) {
        String subjectCode = request.getSubjectCode();
        TransResult transResult = new TransResult(request.getBillNo(),request.getMerchantBillNo(),0,"交易查询中",System.currentTimeMillis());
        if (StringUtils.isBlank(subjectCode)) {
            log.error("subjectCode不能为空!");
            transResult.setMsg("subjectCode不能为空!");
            return transResult;
        }

        //接口安全处理，数据Aes加密、rsa加签名
        //获取本主体的私钥、主体服务端公钥（根据subjectCode获取）
        Subject subject = subjectService.getSubjectByCode(subjectCode);
        if (null == subject) {
            transResult.setMsg("subjectCode未定义，请联系管理员!");
            return transResult;
        }

        String url = subject.getServiceUrl();
        if (StringUtils.isBlank(url)) {
            log.error("主体网关未配置，请联系管理员!");
            transResult.setMsg("主体网关未配置，请联系管理员!");
            return transResult;
        }
        String subjectpayEndPublicKey = subject.getRsaPubKey();
        //获取中台私钥
        String privateKeyStr = systemCfgService.getConfigString(ConfigKeyEnums.PAY_PLATFORM_RSA_PRI_KEY.getCategory(), ConfigKeyEnums.PAY_PLATFORM_RSA_PRI_KEY.getName());
        if (StringUtils.isBlank(privateKeyStr)) {
            log.error("支付中台平台私钥未配置，请联系管理员!");
            transResult.setMsg("支付中台平台私钥未配置，请联系管理员!");
            return transResult;
        }

        String aesKey = RandomStringUtils.randomAlphanumeric(16);
        String reqData = null;
        String oldAesKey = aesKey;
        //使用明文aesKey加密业务数据
        try {
            reqData = AesUtils.encrypt(JSONObject.toJSONString(request), aesKey);
        } catch (Exception e) {
            log.error("AES加密业务数据失败!", e);
            transResult.setMsg("AES加密业务数据失败!");
            return transResult;
        }
        //使用主体服务端公钥加密aesKey
        try {
            aesKey = new String(Base64Utils.encode(RSAUtils.encryptByPublicKey(aesKey.getBytes("UTF-8"), subjectpayEndPublicKey)));
        } catch (Exception e) {
            log.error("RSA加密AES 密钥失败!", e);
            transResult.setMsg("RSA加密AES 密钥失败!");
            return transResult;
        }
        String method = MethodConstants.ORDERQUERY;
        MultiValueMap<String, String> postParameters = new LinkedMultiValueMap<String, String>();
        postParameters.add("aesKey", aesKey);
        postParameters.add("subjectCode", subjectCode);
        postParameters.add("method", method);
        postParameters.add("reqData", reqData);

        Map<String, String> params = new TreeMap<String, String>();
        params.put("aesKey", aesKey);
        params.put("subjectCode", subjectCode);
        params.put("method", method);
        params.put("reqData", reqData);
        String jsoStr = JSONObject.toJSONString(params);

        PrivateKey privateKey = RSAUtils.readPrivateKey(privateKeyStr);
        String sign = null;
        try {
            sign = RSAUtils.sign("SHA1WithRSA", jsoStr.getBytes("UTF-8"), privateKey);
        } catch (Exception e) {
            log.error("rsa签名失败!", e);
            transResult.setMsg("rsa签名失败!");
            return transResult;
        }
        postParameters.add("sign", sign);
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity res = null;
        ThirdQueryBills outThirdBills = new ThirdQueryBills();
        outThirdBills.setBillNo(request.getBillNo());
        outThirdBills.setMerchantBillNo(request.getMerchantBillNo());
        outThirdBills.setStatus(1);
        outThirdBills.setAmount(request.getAmount());
        outThirdBills.setSubjectCode(request.getSubjectCode());
        outThirdBills.setRequestUrl(url);
        outThirdBills.setRequestText(reqData);
        outThirdBills.setBeginTime(System.currentTimeMillis());
        ServiceType serviceType = ServiceType.valueOf(request.getServiceType());
        outThirdBills.setServiceType(serviceType);

        try {
            res = restTemplate.postForObject(url, postParameters, ResponseEntity.class);
            outThirdBills.setStatus(1);
        } catch (Exception e) {
            log.error("调用中台服务端接口失败", e);
            outThirdBills.setStatus(0);
        }
        outThirdBills.setEndTime(System.currentTimeMillis());
        if (null == res) {
            //默认交易处理中
            outThirdBills.setStatus(0);
            log.error("中台服务端接口返回空");
            transResult.setMsg("中台服务端接口返回空");
            transResult.setTransStatus(0);
            try {
                thirdQueryBillsService.insert(outThirdBills);
            } catch (Exception e) {
                log.error("插入第三方请求订单表失败:" + outThirdBills.getBillNo(), e);
            }

            return transResult;
        }
        //TODO 此处后续可优化为异步处理
        try {
            outThirdBills.setResponseText(JSONObject.toJSONString(res));
            thirdQueryBillsService.insert(outThirdBills);
        } catch (Exception e) {
            log.error("插入第三方请求订单表失败:" + outThirdBills.getBillNo(), e);
        }
        if (StringUtils.equals(ResponseEntity.STATUS_FAIL, res.getStatus())) {
            //失败,中台服务一定要保证status=0一定是失败
            transResult.setTransStatus(2);
            transResult.setMsg("中台服务返回交易失败");
            return transResult;
        }
        if (StringUtils.equals(ResponseEntity.STATUS_OK, res.getStatus())) {
            //取data判断
            Object data = res.getData();
            if (null == data) {
                transResult.setTransStatus(0);
                transResult.setMsg("中台服务未返回业务结果");
                return transResult;
            }
            String dataEncrypt = (String) res.getData();
            String dataDecrypt = null;
            //解密
            try {
                dataDecrypt = AesUtils.decrypt(dataEncrypt, oldAesKey);
            } catch (Exception e) {
                log.error("中台返回业务数据Aes解密失败", e);
                transResult.setTransStatus(0);
                transResult.setMsg("中台返回业务数据Aes解密失败");
                return transResult;
            }
            if (StringUtils.isBlank(dataDecrypt)) {
                log.error("中台返回业务数据Aes解密失败");
                transResult.setTransStatus(0);
                transResult.setMsg("中台返回业务数据Aes解密失败");
                return transResult;
            }
            //解密成功转换成transResult
            TransResult resResult = null;
            try {
                resResult = JSONObject.parseObject(dataDecrypt, TransResult.class);
            } catch (Exception e) {
                log.error("中台返回业务数据转换失败", e);
                transResult.setTransStatus(0);
                transResult.setMsg("中台返回业务数据转换失败");
                return transResult;
            }
            if (null == resResult || null == resResult.getTransStatus()) {
                transResult.setTransStatus(0);
                transResult.setMsg("中台服务未返回业务结果");
                return transResult;
            }
            transResult.setTransStatus(resResult.getTransStatus());
            transResult.setTransTime(resResult.getTransTime());
            transResult.setMsg(resResult.getMsg());
            return transResult;
        }
        return transResult;
    }
}
